class SnowflakeIdGenerator {
    private epoch: number;
    private workerIdBits: number;
    private datacenterIdBits: number;
    private sequenceBits: number;

    private maxWorkerId: number;
    private maxDatacenterId: number;
    private maxSequence: number;

    private workerIdShift: number;
    private datacenterIdShift: number;
    private timestampLeftShift: number;

    private workerId: number;
    private datacenterId: number;
    private sequence: number;
    private lastTimestamp: number;

    constructor(epoch: Date, workerId: number, datacenterId: number) {
        this.epoch = epoch.getTime();
        this.workerIdBits = 5;
        this.datacenterIdBits = 5;
        this.sequenceBits = 12;

        this.maxWorkerId = -1 ^ (-1 << this.workerIdBits);
        this.maxDatacenterId = -1 ^ (-1 << this.datacenterIdBits);
        this.maxSequence = -1 ^ (-1 << this.sequenceBits);

        this.workerIdShift = this.sequenceBits;
        this.datacenterIdShift = this.sequenceBits + this.workerIdBits;
        this.timestampLeftShift = this.sequenceBits + this.workerIdBits + this.datacenterIdBits;

        if (workerId > this.maxWorkerId || workerId < 0) {
            throw new Error(`worker Id can't be greater than ${this.maxWorkerId} or less than 0`);
        }
        if (datacenterId > this.maxDatacenterId || datacenterId < 0) {
            throw new Error(`datacenter Id can't be greater than ${this.maxDatacenterId} or less than 0`);
        }

        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = 0;
        this.lastTimestamp = -1;
    }

    private tilNextMillis(lastTimestamp: number): number {
        let timestamp = Date.now();
        while (timestamp <= lastTimestamp) {
            timestamp = Date.now();
        }
        return timestamp;
    }

    public nextId(): number {
        let timestamp = Date.now();

        if (timestamp < this.lastTimestamp) {
            throw new Error('时钟回拨');
        }

        if (this.lastTimestamp === timestamp) {
            this.sequence = (this.sequence + 1) & this.maxSequence;
            if (this.sequence === 0) {
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0;
        }

        this.lastTimestamp = timestamp;

        const id = ((timestamp - this.epoch) << this.timestampLeftShift) |
            (this.datacenterId << this.datacenterIdShift) |
            (this.workerId << this.workerIdShift) |
            this.sequence

        if (id > 0) return id;

        this.tilNextMillis(timestamp + 1)

        return this.nextId();
    }
}

// 使用示例
const epoch = new Date('2025/01/01');
const workerId = 1;
const datacenterId = 1;
const idGenerator = new SnowflakeIdGenerator(epoch, workerId, datacenterId);

export default idGenerator;